#include "TcpServer.h"
#include "Logger.h"
#include "TcpConnection.h"

#include <strings.h>

static EventLoop* CheckLoopNotNull(EventLoop* loop)
{
    if(loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d mainLoop is nullptr!\n", __FILE__, __FUNCTION__, __LINE__);
    }
    return loop;
}

TcpServer::TcpServer(EventLoop* loop, const InetAddress& listenAddr, const std::string& nameArg, Option option)
    : loop_(CheckLoopNotNull(loop))  // 不接受用户传一个空指针
    , ipPort_(listenAddr.toIpPort())
    , name_(nameArg)
    , acceptor_(new Acceptor(loop, listenAddr, option == kReusePort))
    , threadPool_(new EventLoopThreadPool(loop, nameArg))
    , connectionCallback_(/*defaultConnectionCallback*/)
    , messageCallback_(/*defaultMessageCallback*/)
    , nextConnId_(1)
    , started_(0)
{
    // 当有新用户连接时，会执行回调Acceptor::handleRead -> 执行回调newConnectionCallback_ -> 实际执行TcpServer::newConnection
    acceptor_->setNewConnectionCallback(std::bind(&TcpServer::newConnection, this, std::placeholders::_1, std::placeholders::_2));
}

TcpServer::~TcpServer()
{
    LOG_INFO("TcpServer::~TcpServer %s destructing.\n", name_.c_str());

    for(auto& item: connections_)
    {
        TcpConnectionPtr conn(item.second);  // 创建一个新的 TcpConnectionPtr 对象 conn，并将其初始化为 item.second 的副本，conn 和 item.second 将共享同一个 TcpConnection 对象的所有权
        item.second.reset();  // 将 item.second 重置为空指针，这会减少 TcpConnection 对象的引用计数，但不会立即删除对象，因为 conn 仍然持有该对象的引用 → 从 connections_ 容器中移除了对 TcpConnection 对象的引用
        // 销毁连接
        conn->getLoop()->runInLoop(
            std::bind(&TcpConnection::connectDestroyed, conn)
        );
    }  // 出了局部，TcpConnectionPtr conn会自动析构释放资源
}

void TcpServer::setThreadNum(int numThreads)
{
    threadPool_->setThreadNum(numThreads);
}

// 开启服务器监听 调用完start() , 就要loop_.loop()了，这里的loop_是mainloop
void TcpServer::start()
{
    if(started_++ == 0)  // 防止一个TcpServer对象被start多次
    {
        threadPool_->start(threadInitCallback_);  // 启动底层的subloop线程池
        loop_->runInLoop(std::bind(&Acceptor::listen, acceptor_.get()));  // 启动listen监听新用户连接
    }
}

/**
 * @brief 
 * 主线程（mainloop）根据轮训算法选择一个subloop
 * 唤醒subloop
 * 把当前connfd封装成channel，注册一些回调
 * 分发给subloop
 * 根据sockfd，封装TcpConnection对象
 * 这个函数被绑定为新用户连接到来的回调，Acceptor监听到有新用户连接，会执行这个回调，具体在Acceptor::handleRead()中
 * @param sockfd -- 与客户端通信的sockfd
 * @param peerAddr -- 客户端的IpPort
 */
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
    EventLoop* ioLoop = threadPool_->getNextLoop();  // 轮询选择一subloop来管理对应的channel
    char buf[64] = {0};
    snprintf(buf, sizeof buf,"-%s#%d", ipPort_.c_str(), nextConnId_);
    ++nextConnId_;  // nextConnId_只有在mainloop中处理，不涉及线程安全问题
    std::string connName = name_ + buf;  // 格式化连接名称

    LOG_INFO("TcpServer::newConnection [%s] - new connection [%s] from %s.\n", name_.c_str(), connName.c_str(), peerAddr.toIpPort().c_str());

    // 通过sockfd获取其绑定的本机的ip地址和端口信息
    sockaddr_in local;
    ::bzero(&local, sizeof local);
    socklen_t addrlen = sizeof local;

    // getsockname，获取一个套接字的名字，本地地址将被填充至local
    if(::getsockname(sockfd, (sockaddr*)&local, &addrlen) < 0)
    {
        LOG_ERROR("sockets::getLocalAddr error.\n");
    }
    InetAddress localAddr(local);

    // 根据连接成功的sockfd，创建TcpConnection对象
    TcpConnectionPtr conn(new TcpConnection(ioLoop,
                                            connName,
                                            sockfd,
                                            localAddr,
                                            peerAddr));
    
    // 将tcp连接添加到map中
    connections_[connName] = conn;
    // 下面的回调，都是用户设置给TcpServer的 → TcpConnection → Channel → Poller → notify channel调用回调
    conn->setConnectionCallback(connectionCallback_);
    conn->setMessageCallback(messageCallback_);
    conn->setWriteCompleteCallback(writeCompleteCallback_);
    
    // 设置如何关闭连接的回调  conn->shutdonw
    conn->setCloseCallback(
        std::bind(&TcpServer::removeConnection, this, std::placeholders::_1)
    );

    // 直接调用TcpConnection::connectEstablished
    ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
    loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

void TcpServer::removeConnectionInLoop(const TcpConnectionPtr &conn)
{
    LOG_INFO("TcpServer::removeConnectionInLoop [%s] connection %s.\n", name_.c_str(), conn->name().c_str());
    
    size_t n = connections_.erase(conn->name());  // map里删除
    EventLoop* ioLoop = conn->getLoop();  // 获取conn所在的loop
    // 销毁连接
    ioLoop->queueInLoop(
        std::bind(&TcpConnection::connectDestroyed, conn)
    );
}